package cs348a.opengl;

import java.awt.Color;
import java.util.ArrayList;
import java.util.List;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLJPanel;

import cs348a.opengl.object.OpenGLObject;

public class JoglEventListener implements GLEventListener {
	/** The background color */
	private Color bgColor = new Color(0.0f, 0.0f, 0.0f, 0.0f);
	
	/** The OpenGL light */
	public JoglLight light = new JoglLight(GL.GL_LIGHT0);
	
	/** The perspective manager */
	public JoglPerspective perspective = new JoglPerspective();
	
	/** The object list */
	private List<OpenGLObject> objectList = new ArrayList<OpenGLObject>();
	
	/** Rotation of all objects(world). This is a quick fix for rotation. */
	public float[] rotation = new float[3];
	
	@Override
	public void display(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
		
		// Clear The Screen And The Depth Buffer
		// clear GL
		if ((drawable instanceof GLJPanel)
				&& !((GLJPanel) drawable).isOpaque()
				&& ((GLJPanel) drawable).shouldPreserveColorBufferIfTranslucent()) {
			gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
		} else {
			gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		}
		gl.glLoadIdentity();
		
		light.displayLight(drawable);

		// update perspective
		double ratio = drawable.getWidth()/(double)drawable.getHeight();
		perspective.updatePerspective(drawable, ratio);
		perspective.updateView(drawable);
		
		// rotate
		gl.glRotatef(-rotation[1], 0, 1, 0);
		gl.glRotatef(-rotation[0], 1, 0, 0);
		gl.glRotatef(-rotation[2], 0, 0, 1);
		
		// draw objects here
		for(OpenGLObject object : objectList){
			object.displayComponent(drawable);
		}

		// reset the rotation
		gl.glRotatef(rotation[2], 0, 0, 1);
		gl.glRotatef(rotation[0], 1, 0, 0);
		gl.glRotatef(rotation[1], 0, 1, 0);
	}

	@Override
	public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void init(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
		
		// use smooth shading
		gl.glShadeModel(GL.GL_SMOOTH);
		
		// background
		gl.glClearColor(bgColor.getRed()/255f, bgColor.getGreen()/255f, 
				bgColor.getBlue()/255f, bgColor.getAlpha()/255f);

		// enable alpha blending
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
		gl.glEnable(GL.GL_BLEND);
		
		gl.glClearDepth(1.0f);			// Depth Buffer Setup

		gl.glEnable(GL.GL_DEPTH_TEST);	// Enables Depth Testing
		gl.glDepthFunc(GL.GL_LEQUAL);	// The Type Of Depth Test To Do

		// set light 
		light.initLight(drawable);

		gl.glEnable(GL.GL_COLOR_MATERIAL);
		gl.glColorMaterial(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE);
        
        gl.glEnable(GL.GL_NORMALIZE);
		
		// perspective hints
		gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_FASTEST);
	}

	@Override
	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
		GL gl = drawable.getGL();
		
		// prevent divide-by-zero
		if (height == 0) {
			height = 1;
		}
		
		// reset the current viewport
		gl.glViewport(x, y, width, height);
		
		// update perspective
		double ratio = width/(double)height;
		perspective.updatePerspective(drawable, ratio);
	}
	
	/**
	 * Adds the <code>OpenGLObject</code>.
	 * 
	 * @param object
	 */
	public synchronized void addOpenGLObject(OpenGLObject object){
//		objectList.add(object);
		objectList.add(0, object);
	}
	
	/**
	 * Removes the <code>OpenGLObject</code>.
	 * 
	 * @param object
	 */
	public synchronized void removeOpenGLObject(OpenGLObject object){
		objectList.remove(object);
	}
	
	/**
	 * Removes all <code>OpenGLObject</code>s.
	 */
	public synchronized void removeAllOpenGLObjects(){
		objectList.clear();
		
		System.gc();
	}
}
